package com.user_hosted_storage.server.dto;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.Entity;

import com.user_hosted_storage.dto.DTO;
import com.user_hosted_storage.dto.annotations.ReadOnly;
import com.user_hosted_storage.dto.annotations.Transient;

public class DTOConverter {

	public static <EntityClass, DTOClass extends DTO>  EntityClass convertToEntity(DTOClass dto, Class<EntityClass> entityClass) {
		
		try {
			EntityClass entity = entityClass.newInstance();
			Field[] fields = dto.getClass().getDeclaredFields();
			
			for (Field field : fields) {
				try {
					Field entityField = entityClass.getDeclaredField(field.getName());
					
					if(field.getAnnotation(ReadOnly.class) != null){
						continue;
					}
					Object value = field.get(dto);
					
					entityField.set(entity, value);					
				} catch (NoSuchFieldException e) {
				}
			}
			
			return entity;
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		
		return null;
	}
	
	public static <EntityClass, DTOClass extends DTO>  EntityClass convertToEntityFull(DTOClass dto, Class<EntityClass> entityClass) {
		
		try {
			
			EntityClass entity = entityClass.newInstance();
			Field[] fields = dto.getClass().getDeclaredFields();
			
			for (Field field : fields) {
				
				try {
					Field entityField = entityClass.getDeclaredField(field.getName());
					
					Object value = field.get(dto);
					
					entityField.set(entity, value);					
				} catch (NoSuchFieldException e) {
				}
			}
			
			return entity;
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		
		return null;
	}
	
	@SuppressWarnings("unchecked")
	public static <EntityClass, DTOClass extends DTO>  DTOClass convertToDTO(EntityClass entity, Class<DTOClass> dtoClass) {
		
		try {
			DTOClass dto = dtoClass.newInstance();
			List<Field> fields = getAllFields(entity.getClass());
			
			for (Field field : fields) {
				field.setAccessible(true);
				try {
					Field dtoField = getField(dtoClass,field.getName());
					dtoField.setAccessible(true);
					if(dtoField.getAnnotation(Transient.class) != null){
						continue;
					}
					Object value = field.get(entity);
					if(value == null) {
						continue;
					}
					
					if(value.getClass().getAnnotation(Entity.class) != null){
						dtoField.set(dto, convertToDTO(value, (Class<DTO>)dtoField.getType()));
					} else {
						dtoField.set(dto, value);
					}
				} catch (NoSuchFieldException e) {
				}
			}
			
			return dto;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	public static <EntityClass, DTOClass>  DTOClass convertToDTOFull(EntityClass entity, Class<DTOClass> dtoClass) {
		
		try {
			DTOClass dto = dtoClass.newInstance();
			List<Field> fields = getAllFields(entity.getClass());
			
			for (Field field : fields) {
				field.setAccessible(true);
				try {
					Field dtoField = getField(dtoClass,field.getName());
					dtoField.setAccessible(true);
					Object value = field.get(entity);
					if(value == null) {
						continue;
					}
					if(value.getClass().getAnnotation(Entity.class) != null){
						dtoField.set(dto, convertToDTOFull(value, dtoField.getType()));
					} else {
						dtoField.set(dto, value);
					}					
				} catch (NoSuchFieldException e) {}
			}
			
			return dto;
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {}		
		return null;
	}

	private static List<Field> getAllFields(Class<?> clazz) {
		List<Field> allFields = new LinkedList<Field>();
		
		Field[] declared = clazz.getDeclaredFields();
		
		for (Field field : declared) {
			allFields.add(field);
		}
		
		Class<?> superClazz = clazz.getSuperclass();
		if(!superClazz.equals(Object.class)) {
			allFields.addAll(getAllFields(superClazz));
		}
		
		return allFields;
	}
	
	private static Field getField(Class<?> clazz, String name) throws NoSuchFieldException {
		if(clazz.equals(Object.class)) {
			throw new NoSuchFieldException();
		}
		try {
			Field field = clazz.getDeclaredField(name);
			return field;
		} catch (NoSuchFieldException e) {
			return getField(clazz.getSuperclass(), name);
		}

	}
	
}
